import * as https from 'https';
import { format } from "util";
import * as crypto from 'crypto';
import * as Koa from 'koa';
import { ServiceBase, Exceptions } from './../kalend';
import { weChatTokenSchema } from '../model/webchat'
import config from '../middleware/config';
import { customer_accountService } from './customer_account';
// var wxpay = require('./middleware/wxpay');
import { wxpay } from '../middleware/wxpay';
import * as  request from 'request';
import { customer_orderService } from './customer_order';
var xml2json = require("xmlparser");
var contentType = require('content-type')
import * as getRawBody from 'raw-body';
import { common } from '../middleware/common';
import { usertokenService } from './usertoken';
import { qrcodeService } from './qrcode';
import { logService } from './log';
import { paydatalogService } from './paydatalog';
import { datadictionaryService } from './datadictionary';
var xmlreader = require("xmlreader");
//const url = require('url')
import * as QRCodetorul from "qrcode";
import { fsfiles } from '../middleware/fsFiles';
import { merchant_wineService } from './merchant_wine';
import { merchantService } from './merchant';
import { ObjectId } from 'bson';

class WechatService extends ServiceBase {
    //设置 WeChat 对象属性 config
    configjson: any = config.get("wechat");
    // 设置随机数
    noncestr: any = this.configjson["noncestr"];
    //设置 WeChat 对象属性 appID
    appID: any = this.configjson["appid"];
    //设置 WeChat 对象属性 appScrect
    appScrect: any = this.configjson["appsecret"];
    //设置 WeChat 对象属性 accessTokenUrl
    accessTokenUrl: any = this.configjson["accessTokenUrl"] + "?grant_type=%s&appid=%s&secret=%s";
    //设置 WeChat 对象属性 sessionUrl
    sessionUrl: any = this.configjson["sessionUrl"] + "?appid=%s&secret=%s&js_code=%s&grant_type=%s";
    //设置统一支付接口地址
    paytotal: any = this.configjson["paytotal"];
    // 商户id
    wx_mch_id: any = this.configjson["wx_mch_id"];
    // 商户密钥
    wx_mch_key: any = this.configjson["wx_mch_key"];
    // 支付回调函数
    notify_url: any = this.configjson["notify_url"];
    // 支付body
    paybody: any = this.configjson["paybody"];
    // 支付方式
    trade_type: any = this.configjson["trade_type"];
    // 二维码地址
    qrcodeurl: any = this.configjson["qrcodeurl"];
    // 设置 腾讯获取经纬度API
    locationUrl: any = this.configjson["locationUrl"] + "?address=%s&key=%s";
    // 设置 腾讯获取经纬度KEY
    locationKey: any = this.configjson["locationKey"];

    constructor() {
        super(weChatTokenSchema);
    }

    async requestGet(url: any) {
        return new Promise(function (resolve, reject) {
            https.get(url, function (res) {
                var buffer: any = [];
                var size = 0;
                //监听 data 事件
                res.on('data', function (data) {
                    buffer.push(data);
                    size += data.length;
                });
                //监听 数据传输完成事件
                res.on('end', function () {
                    let result = Buffer.concat(buffer, size).toString('utf-8');
                    resolve(result);
                });
            }).on('error', function (err) {
                reject(err);
            });
        });
    }
    async requestPOST(url: any, formData: any) {
        return new Promise((resolve, reject) => {
            request({ url: url, method: 'POST', body: formData }, (err, response, body) => {
                if (!err && response.statusCode == 200) {
                    resolve(response);
                } else {
                    reject(err);
                }
            });
        });
    }
    /**
     * 获取微信 access_token,未过期取数据库，过期后重新获取
     */
    async getAccessToken() {
        let that = this;
        var res: any = await that.Model.find({}).sort({ cache_duration: -1 }).limit(1);
        let accessTokenJson: any = res[0];

        return await new Promise(function (resolve, reject) {
            //获取当前时间 
            var currentTime = new Date().getTime();
            //格式化请求地址
            var url = format(that.accessTokenUrl, 'client_credential', that.appID, that.appScrect);
            console.log(accessTokenJson);
            //判断 本地存储的 access_token 是否有效
            if (accessTokenJson == undefined || accessTokenJson.cache_duration < currentTime) {
                that.requestGet(url).then(function (data: any) {
                    var result = JSON.parse(data);
                    console.log("获取小程序签名返回：" + result);
                    if (data.indexOf("errcode") < 0) {
                        let expires_time = new Date().getTime() + (parseInt(result.expires_in) - 200) * 1000;
                        //更新本地存储的
                        that.setToken(url, result.access_token, expires_time, function () { });
                        //将获取后的 access_token 返回
                        resolve(result.access_token);
                    } else {
                        //将错误返回
                        resolve(result.access_token);
                    }
                });
            } else {
                //将本地存储的 access_token 返回
                resolve(accessTokenJson.access_token);
            }
        });
    }

    /**
     * 根据URLcode获取对应的openid
     * https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
     */
    async getOpenidByCode(code: any) {
        let that = this;
        let res: any = {};
        if (code) {
            let result: any = await new Promise((resolve, reject) => {
                //格式化请求地址
                var url = format(that.sessionUrl, that.appID, that.appScrect, code, "authorization_code");
                //判断 本地存储的 access_token 是否有效
                return that.requestGet(url).then((data: any) => {
                    var res = JSON.parse(data);
                    console.log(res);
                    //将错误返回
                    resolve(res);
                });
            });
            if (result.openid) {
                // 保存用户信息
                //let userinit = await customer_accountService.creatnew({ appcode: code, openid: result.openid });
                // 返回用户登录态key随机子串
                res.token = common.createtokenStr();
                // 保存用户会话信息
                await usertokenService.create({
                    tokencode: res.token,
                    openid: result.openid,
                    appcode: result.code,
                    session_key: result.session_key,
                    remarks: '用户登录获取'
                });
                return res;
            } else {
                throw Exceptions.unknown(result);
            }
        } else {
            throw Exceptions.known('未取到用户凭证code');
        }
    }
    /**
     * 解密数据
     * @param token 
     * @param encryptedData 
     * @param iv 
     * @param code 
     */
    async decodeData(token: any, encryptedData: any, iv: any) {
        if (!token) {
            throw Exceptions.notoken();
        }
        let tokendata = (await usertokenService.getToken(token));
        var data = await this.decryptData(tokendata.session_key, encryptedData, iv);

        console.log('解密后 data: ', data)
        console.log('查询用户不存在，创建新用户');
        customer_accountService.creatnew({ phone: data.phoneNumber, openid: tokendata.openid, appcode: tokendata.appcode, session_key: tokendata.session_key, tokenid: tokendata._id });
        return data;
    }
    /**
     * 申请支付
     * @param mid 
     * @param uid 
     * @param money 
     * @param conins 
     * @param ctx 
     */
    async pay(mid: any, token: any, money: number, conins: number, person: number, winelist: Array<any>, meal: number, winemoney: number, maxconins: number, paytype: string, mealmoney: number, ctx: Koa.Context) {
        let result: any = {};
        if (!token) {
            throw Exceptions.notoken();
        }
        const sesiondata = await usertokenService.getToken(token);
        // 支付
        if (paytype === 'pay') {
            const orderCode = await customer_orderService.buildorder(mid, sesiondata.userid, money, conins, person, winelist, meal, sesiondata.openid, winemoney, mealmoney, "买单");
            // 调微信统一下单接口
            console.log('买单：调统一下单接口');
            result = this.unifiedorder(sesiondata.openid, money, orderCode, "随餐饮艺餐费", ctx);
        } else if (paytype === 'payok') {
            const orderCode = await customer_orderService.buildorder(mid, sesiondata.userid, money, conins, person, winelist, meal, sesiondata.openid, winemoney, mealmoney, "买单");
            // 直接生成完成状态的订单，自动执行回调
            // 更新订单状态
            console.log('买单：支付金额为0，自动执行回调');
            customer_orderService.successOrder(orderCode);

            result = {
                paytype: 'payok',
                state: 'show',
                orderCode: orderCode,
                msg: '支付成功'
            };
        } else {
            const addmealmoney = common.sub(mealmoney, meal || 0);
            if (conins > 0) {
                let userdata = await customer_accountService.querySingle({ _id: sesiondata.userid }, { select: 'coinbalance' });
                return await this.getwinemoney(conins, maxconins, userdata, winelist, mid, addmealmoney);
            } else {
                if (money > 0) {
                    result = {
                        paytype: 'pay',
                        state: 'show',
                        winemoney: 0,
                        coninsnum: 0,
                        msg: `您本次消费需支付金额${money}元，欢迎下次光临`
                    };
                } else {
                    result = {
                        paytype: 'payok',
                        state: 'show',
                        winemoney: 0,
                        coninsnum: 0,
                        msg: '您本次消费需支付金额为0，欢迎下次光临'
                    };
                }
            }
        }
        return result;
    }

    /**
     * 生成订单接口
     */
    async buildorder(token: any, mid: any, winelist: Array<any>, mealmoney: number, ctx: Koa.Context) {
        let result: any = {};
        if (!token) {
            throw Exceptions.notoken();
        }
        const sesiondata = await usertokenService.getToken(token);
        let userdata = await customer_accountService.querySingle({ _id: sesiondata.userid }, { select: 'coinbalance mealbalance' });
        let merdata = await merchantService.querySingle({ _id: mid }, { select: '' });
        return result;
    }

    async getwinemoney(conins: number, maxconins: number, userdata: any, winelist: Array<any>, mid: any, addmealmoney: number) {
        let addmoney = 0;
        let result: any = {
            paytype: 'pay',
            state: 'show',
            coninsnum: 0,
            winemoney: addmoney
        };
        const wids = winelist.map((a) => {
            return new ObjectId(a._id);
        })
        const wines = await merchant_wineService.getWines(wids);
        let trueconins = 0; // 实际消费酒币数量

        if (conins <= maxconins) {
            if (conins > userdata.coinbalance) {
                trueconins = userdata.coinbalance;
            } else {
                trueconins = conins;
            }
        } else {
            if (maxconins > userdata.coinbalance) {
                trueconins = userdata.coinbalance;
            } else {
                trueconins = maxconins;
            }
        }

        let cconis = 0;
        let cc = 0;
        let wincount = 0;
        console.log('实际可抵扣酒币数：' + trueconins);
        wines.forEach((item) => {
            const w = winelist.find((a) => {
                return a._id === item._id.toJSON();
            });
            if (w) {
                wincount += w.count;
                for (let index = 1; index <= w.count; index++) {
                    for (let n = 1; n <= item.num; n++) {
                        if ((cconis + 1) <= trueconins) {
                            cc += 1;
                            cconis += 1;
                            console.log('抵扣酒币+1：' + cc)
                        } else {
                            addmoney += item.order;
                        }
                    }
                }
            }
        });
        result.state = 'show';
        result.coninsnum = cconis;
        let ms = `本次消费酒饮${wincount}瓶(${conins}酒币)，已抵扣${cc}酒币，还需支付餐费${addmealmoney}元，`;
        if (addmoney > 0) {
            ms += `补酒饮差价${addmoney}元，`;
        }
        const moneytotal = common.add(addmealmoney, addmoney);
        ms += `共计${moneytotal}元。`;
        result.msg = ms;
        result.winemoney = addmoney;
        return result;
    }

    async getwinemoney_bak(conins: number, maxconins: number, userdata: any, winelist: Array<any>, mid: any, addmealmoney: number) {
        let addmoney = 0;
        let result: any = {
            paytype: 'pay',
            state: 'show',
            coninsnum: 0,
            winemoney: addmoney
        };
        const wids = winelist.map((a) => {
            return a._id;
        })
        const wines = await merchant_wineService.queralls(
            {
                merchantid: mid,
                _id: { $in: wids }
            },
            'price', { price: 1 });
        let winpricelist: Array<any> = [];
        winelist.map((a) => {
            for (let index = 0; index < a.count; index++) {
                let win = wines.find((w) => {
                    return w._id.toJSON() === a._id;
                });
                if (win) {
                    winpricelist.push(win.price || 0);
                }
            }
        });
        winpricelist = winpricelist.sort((a, b) => {
            return b - a;
        });
        if (conins <= maxconins) {
            if (conins > userdata.coinbalance) {
                const spl = conins - userdata.coinbalance;
                const addwines = winpricelist.splice(winpricelist.length - spl, winpricelist.length);
                addwines.map((a) => {
                    addmoney = common.add(a, addmoney);
                });

                result.state = 'show';
                result.coninsnum = userdata.coinbalance;
                const moneytotal = common.add(addmealmoney, addmoney);
                result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${userdata.coinbalance}瓶，还需支付餐费${addmealmoney}元，酒水费${addmoney}元，共计${moneytotal}元`;
                //result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${userdata.coinbalance}瓶，还需支付餐费${addmealmoney}元，还需支付${spl}瓶共计${addmoney}元`;
            } else {
                result.state = 'show';
                result.coninsnum = conins;
                result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${conins}瓶，还需支付餐费${addmealmoney}元，酒水费0元，共计${addmealmoney}元`;
                // result.msg = `本次消费酒水${conins}瓶，酒币已全部抵扣`;
            }
        } else {
            if (maxconins > userdata.coinbalance) {
                const spl = conins - userdata.coinbalance;
                const addwines = winpricelist.splice(winpricelist.length - spl, winpricelist.length);
                addwines.map((a) => {
                    addmoney = common.add(a, addmoney);
                });
                result.state = 'show';
                result.coninsnum = userdata.coinbalance;
                const moneytotal = common.add(addmealmoney, addmoney);
                result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${userdata.coinbalance}瓶，还需支付餐费${addmealmoney}元，酒水费${addmoney}元，共计${moneytotal}元`;
                //result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${userdata.coinbalance}瓶，还需支付${spl}瓶共计${addmoney}元`;
            } else {
                const spl = conins - maxconins;
                const addwines = winpricelist.splice(winpricelist.length - spl, winpricelist.length);
                addwines.map((a) => {
                    addmoney = common.add(a, addmoney);
                });
                result.state = 'show';
                result.coninsnum = maxconins;
                const moneytotal = common.add(addmealmoney, addmoney);
                result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${maxconins}瓶，还需支付餐费${addmealmoney}元，酒水费${addmoney}元，共计${moneytotal}元`;
                // result.msg = `本次消费酒水${conins}瓶，酒币可抵扣${maxconins}瓶，还需支付${spl}瓶共计${addmoney}元`;
            }
        }
        result.winemoney = addmoney;
        return result;
    }

    // 支付金额为0确认抵扣方法
    // async PayOk(mid: any, token: any, money: number, conins: number, person: number, winelist: Array<any>, meal: number, winemoney: number, maxconins: number, ctx: Koa.Context) {
    //     if (!token) {
    //         throw Exceptions.notoken();
    //     }
    //     const sesiondata = await usertokenService.getToken(token);
    //     let userdata = await customer_accountService.querySingle({ _id: sesiondata.userid }, { select: 'coinbalance mealbalance' });
    //     if (conins <= maxconins) {
    //         if (conins > userdata.coinbalance) {
    //             const wids = winelist.map((a) => {
    //                 return a._id;
    //             })
    //             const wines = await merchant_wineService.queralls(
    //                 {
    //                     merchantid: mid,
    //                     _id: { $in: wids }
    //                 },
    //                 'price', { price: 1 });
    //             let winpricelist: Array<any> = [];
    //             winelist.map((a) => {
    //                 for (let index = 0; index < a.count; index++) {
    //                     let win = wines.find((w) => {
    //                         return w._id.toJSON() === a._id;
    //                     });
    //                     if (win) {
    //                         winpricelist.push(win.price || 0);
    //                     }
    //                 }
    //             });
    //             winpricelist = winpricelist.sort((a, b) => {
    //                 return a - b;
    //             })
    //             const spl = conins - userdata.coinbalance;
    //             const addwines = winpricelist.splice(winpricelist.length - spl, winpricelist.length);
    //             let addmoney = 0;
    //             addwines.map((a) => {
    //                 addmoney = common.add(a, addmoney);
    //             });
    //             return {
    //                 msgcode: 'fail',
    //                 winemoney: addmoney,
    //                 msg: `本次酒币最多可抵扣${userdata.coinbalance}瓶，还需支付${spl}瓶共计[${addmoney}]元`
    //             };
    //         }
    //     } else if (conins > maxconins) {
    //         if (maxconins <= userdata.coinbalance) {
    //             const wids = winelist.map((a) => {
    //                 return a._id;
    //             })
    //             const wines = await merchant_wineService.queralls(
    //                 {
    //                     merchantid: mid,
    //                     _id: { $in: wids }
    //                 },
    //                 'price', { price: 1 });
    //             let winpricelist: Array<any> = [];
    //             winelist.map((a) => {
    //                 for (let index = 0; index < a.count; index++) {
    //                     let win = wines.find((w) => {
    //                         return w._id.toJSON() === a._id;
    //                     });
    //                     if (win) {
    //                         winpricelist.push(win.price || 0);
    //                     }
    //                 }
    //             });
    //             winpricelist = winpricelist.sort((a, b) => {
    //                 return a - b;
    //             })
    //             const spl = conins - maxconins;
    //             const addwines = winpricelist.splice(winpricelist.length - spl, winpricelist.length);
    //             let addmoney = 0;
    //             addwines.map((a) => {
    //                 addmoney = common.add(a, addmoney);
    //             });
    //             return {
    //                 msgcode: 'fail',
    //                 winemoney: addmoney,
    //                 msg: `本次酒币最多可抵扣${maxconins}瓶，还需支付${spl}瓶共计[${addmoney}]元`
    //             };
    //         } else {
    //             const wids = winelist.map((a) => {
    //                 return a._id;
    //             })
    //             const wines = await merchant_wineService.queralls(
    //                 {
    //                     merchantid: mid,
    //                     _id: { $in: wids }
    //                 },
    //                 'price', { price: 1 });
    //             let winpricelist: Array<any> = [];
    //             winelist.map((a) => {
    //                 for (let index = 0; index < a.count; index++) {
    //                     let win = wines.find((w) => {
    //                         return w._id.toJSON() === a._id;
    //                     });
    //                     if (win) {
    //                         winpricelist.push(win.price || 0);
    //                     }
    //                 }
    //             });
    //             winpricelist = winpricelist.sort((a, b) => {
    //                 return a - b;
    //             })
    //             const spl = conins - userdata.coinbalance;
    //             const addwines = winpricelist.splice(winpricelist.length - spl, winpricelist.length);
    //             let addmoney = 0;
    //             addwines.map((a) => {
    //                 addmoney = common.add(a, addmoney);
    //             });
    //             return {
    //                 msgcode: 'fail',
    //                 winemoney: addmoney,
    //                 msg: `本次酒币最多可抵扣${userdata.coinbalance}瓶，还需支付${spl}瓶共计[${addmoney}]元`
    //             };
    //         }
    //     }
    //     else if (meal > userdata.mealbalance) {
    //         return {
    //             msgcode: 'fail',
    //             winemoney: meal - userdata.mealbalance,
    //             msg: `餐币余额不足，还需支付[${meal - userdata.mealbalance}]元`
    //         };
    //         // return {
    //         //     msgcode: 'fail',
    //         //     msg: '餐币余额不足'
    //         // };
    //         //throw Exceptions.known("餐币余额不足");
    //     }
    //     else if (money > 0) {
    //         const orderCode = await customer_orderService.buildorder(mid, sesiondata.userid, money, conins, person, winelist, meal, sesiondata.openid, winemoney, "买单");
    //         // 调微信统一下单接口
    //         console.log('买单：调统一下单接口');
    //         return this.unifiedorder(sesiondata.openid, money, orderCode, "随餐饮艺餐费", ctx);
    //     } else {
    //         const orderCode = await customer_orderService.buildorder(mid, sesiondata.userid, money, conins, person, winelist, meal, sesiondata.openid, winemoney, "买单");
    //         // 直接生成完成状态的订单，自动执行回调
    //         // 更新订单状态
    //         console.log('买单：支付金额为0，自动执行回调');
    //         customer_orderService.successOrder(orderCode);

    //         return {
    //             orderCode: orderCode,
    //             msg: '支付成功'
    //         };
    //     }
    // }

    // 支付回调函数
    async order_notice(ctx: Koa.Context) {
        // 记录回调日志
        logService.createlogger(ctx, 0);
        // 返回微信消息
        let sendData: any = {
            return_code: 'FAIL',
            return_msg: 'OK'
        }
        console.log('获取回调参数:' + ctx.request.body);
        logService.create({
            title: '获取回调参数',
            detail: ctx.request.body
        });
        xmlreader.read(ctx.request.body, async (errors: any, res: any) => {
            if (null !== errors) {
                console.log(errors)
                return;
            }

            let response = res.xml;
            console.log('读取回调参数:' + response);
            logService.create({
                title: '读取回调参数',
                detail: response
            });
            if (response) {
                console.log('订单号' + response.transaction_id.text());
                let redata: any = {
                    appid: response.appid.text(),
                    bank_type: response.bank_type.text(),//付款银行
                    cash_fee: response.cash_fee.text(),//现金支付金额
                    fee_type: response.fee_type.text(),//货币种类 CNY人民币
                    is_subscribe: response.fee_type.text(),//是否关注公众账号
                    mch_id: response.mch_id.text(),//商户号
                    nonce_str: response.nonce_str.text(),
                    openid: response.openid.text(),
                    //sign: response.sign.text(),
                    out_trade_no: response.out_trade_no.text(),//商户订单号
                    result_code: response.result_code.text(),//业务结果
                    return_code: response.return_code.text(),//返回状态码
                    time_end: response.time_end.text() + '',//支付完成时间
                    total_fee: response.total_fee.text(),//总金额
                    trade_type: response.trade_type.text(),//交易类型
                    transaction_id: response.transaction_id.text()//微信支付订单号
                };

                if (!redata.transaction_id) {
                    sendData.return_code = 'FAIL';
                    sendData.return_msg = '支付结果中微信订单号不存在';
                    logService.create({
                        applicationName: "scyy.app",
                        type: 'info',
                        module: 'wechat',
                        title: 'order_notice',
                        detail: sendData,
                        success: false
                    });
                } else {
                    // 查询是否处理过订单
                    if (!await this.wxorderstatus(redata.transaction_id)) {
                        // 验证签名
                        const jsonsign = response.sign.text();
                        //var sign = wxpay.makesign(redata, this.wx_mch_key);
                        //if (sign == jsonsign) {
                        // 查询订单是否支付成功
                        const orderstate = await this.wxorderquery(redata.transaction_id || redata.out_trade_no);
                        if (orderstate) {
                            redata.sign = jsonsign;
                            redata.status = 1;
                            paydatalogService.create(redata);
                            console.log('yes');
                            // 更新订单状态
                            customer_orderService.updateOrder(redata.out_trade_no, redata);
                            sendData.return_code = 'SUCCESS';
                            console.log('查询成功');
                        } else {
                            sendData.return_msg = '订单查询失败';
                            console.log('订单查询失败');
                        }
                        // } else {
                        //     sendData.return_msg = '订单回调时签名校验失败';
                        //     console.log('订单回调时签名校验失败');
                        // }
                    }
                    else {
                        sendData.return_code = 'SUCCESS';
                        console.log('订单已处理' + redata.transaction_id);
                    }
                }
            }

            let a = wxpay.json2Xml(sendData);
            console.log('返回微信信息： ' + a);
            return a;
        });
    }
    // 查询订单是否存在
    async wxorderquery(transaction_id: any) {
        const that = this;
        let resState = false;
        const wx_mch_key = that.wx_mch_key;
        let pars: any = {
            appid: that.appID,
            mch_id: that.wx_mch_id,
            nonce_str: wxpay.createNonceStr(),
            sign_type: 'MD5',
            transaction_id: transaction_id
        };
        pars.sign = wxpay.makesign(pars, wx_mch_key);

        let formData = wxpay.json2Xml(pars);
        //console.log('调用查询订单接口参数：' + formData);
        var url = 'https://api.mch.weixin.qq.com/pay/orderquery';
        if (url) { } else {
            throw Exceptions.known("查询订单接口未配置");
        }
        let resdata: any = await that.requestPOST(url, formData);
        //console.log('调用查询订单接口返回：' + resdata);
        if (resdata.statusCode == 200) {
            //console.log('调用查询订单接口返回：' + resdata.body);
            var res = xml2json.parser(resdata.body).xml;
            if (res.return_code === 'SUCCESS') {
                if (res.result_code === 'SUCCESS') {
                    resState = true;
                    // 支付成功
                    // if (res.trade_state === 'SUCCESS') {
                    //     resState = true;
                    //     console.log(`订单交易状态:` + res.trade_state);
                    //     console.log(`订单交易描述:` + res.trade_state_desc);
                    // } else {
                    //     console.log(`订单交易状态:` + res.trade_state);
                    //     console.log(`订单交易描述:` + res.trade_state_desc);
                    //     //throw Exceptions.known(`${res.trade_state}---${res.trade_state_desc}`);
                    // }
                } else {
                    console.log(`${res.err_code}:` + res.err_code_des);
                    //throw Exceptions.unknown({ errcode: res.err_code, errmsg: res.err_code_des });
                }
            } else {
                console.log(res.return_msg);
                //throw Exceptions.known(res.return_msg);
            }
        } else {
            console.log(resdata);
            //throw Exceptions.unknown(resdata);
        }
        return resState;
    }
    // 查询订单是否已经处理
    async wxorderstatus(transaction_id: any) {
        return paydatalogService.exist({ transaction_id: transaction_id, status: 1 });
    }
    /**
     * 更新数据库小程序token
     * @param {*} access_token 
     * @param {*} jsapi_ticket 
     * @param {*} cb 
     */
    async setToken(jsapi_ticket: any, access_token: any, cache_duration: any, cb: any) {
        // 有则更新，无则添加
        var query = {
            jsapi_ticket: jsapi_ticket
        };
        var save = {
            jsapi_ticket: jsapi_ticket,
            cache_duration: cache_duration,
            access_token: access_token
        }
        var options = {
            upsert: true
        };
        var result = await this.Model.update(query, save, options, function (err: any, result: any) {
            if (err) throw err;
            return cb(null);
        });
        return result;
    }
    // 统一下单接口
    async unifiedorder(openid: any, money: any, orderCode: any, paybody: any, ctx: Koa.Context) {
        const that = this;
        var appid = that.appID;
        var mchid = that.wx_mch_id;
        var mchkey = that.wx_mch_key;
        var wxurl = that.notify_url; // 支付回调函数地址

        //首先生成签名sign
        let mch_id = mchid;
        let nonce_str = wxpay.createNonceStr();
        let timestamp = wxpay.createTimeStamp();
        //升级会员和支付名称不一样
        // let body = that.paybody;
        let body = paybody;
        let out_trade_no = orderCode;
        let total_fee = wxpay.getmoney(money);
        let spbill_create_ip = ctx.req.connection.remoteAddress;
        let notify_url = wxurl; // 支付成功回调函数
        let trade_type = that.trade_type;

        let sign = wxpay.paysignjsapi(appid, body, mch_id, nonce_str, notify_url, openid, out_trade_no, spbill_create_ip, total_fee, trade_type, mchkey);

        //组装xml数据
        var formData = "<xml>";
        formData += "<appid>" + appid + "</appid>";  //appid
        formData += "<body><![CDATA[" + body + "]]></body>";
        formData += "<mch_id>" + mch_id + "</mch_id>";  //商户号
        formData += "<nonce_str>" + nonce_str + "</nonce_str>"; //随机字符串，不长于32位。
        formData += "<notify_url>" + notify_url + "</notify_url>";
        formData += "<openid>" + openid + "</openid>";  //openid
        formData += "<out_trade_no>" + out_trade_no + "</out_trade_no>";
        formData += "<spbill_create_ip>" + spbill_create_ip + "</spbill_create_ip>";
        formData += "<total_fee>" + total_fee + "</total_fee>";
        formData += "<trade_type>" + trade_type + "</trade_type>";
        formData += "<sign>" + sign + "</sign>";
        formData += "</xml>";

        console.log('调用统一下单接口参数：' + formData);
        var url = that.paytotal; //'https://api.mch.weixin.qq.com/pay/unifiedorder';
        if (url) { } else {
            throw Exceptions.known("统一下单接口未配置");
        }
        let resdata: any = await that.requestPOST(url, formData);
        if (resdata.statusCode == 200) {
            console.log('调用统一下单接口返回：' + resdata.body);
            var data = xml2json.parser(resdata.body.toString("utf-8")).xml;
            var prepay_id = data.prepay_id;
            console.log('解析后的prepay_id==', prepay_id);

            //将预支付订单和其他信息一起签名后返回给前端
            let finalsign = wxpay.paysignjsapifinal(appid, mch_id, prepay_id, nonce_str, timestamp, mchkey, total_fee);

            let rr = {
                paytype: 'pay',
                'orderCode': orderCode,
                'timeStamp': timestamp,
                'nonceStr': nonce_str,
                'package': `prepay_id=${prepay_id}`,
                'signType': 'MD5',
                'paySign': finalsign
            };
            console.log('下单返回：' + rr.toString());
            logService.createlogger(ctx, 0);
            return rr;
        } else {
            console.log(resdata);
            resdata.paytype = 'error';
            return resdata;
            //throw Exceptions.unknown(resdata);
        }
    }

    // 充值成为会员
    async membership(token: any, ctx: Koa.Context, type?: any) {
        if (!token) {
            throw Exceptions.notoken();
        }

        let datdic = await datadictionaryService.querybyKey('membership');
        if (!datdic) {
            throw Exceptions.known('升级会员信息未设置，请联系管理员');
        }
        let money = (datdic.find((a: any) => {
            return a.code == 'money';
        })).count;
        let shipname = '升级会员';
        if (type === 'super') {
            money = (datdic.find((a: any) => {
                return a.code == 'supermoney';
            })).count;
            shipname = '升级白金会员';
        }
        if (type === 'diamond') {
            money = (datdic.find((a: any) => {
                return a.code == 'diamondmoney';
            })).count;
            shipname = '升级钻石会员';
        }
        const sesiondata = await usertokenService.getToken(token);
        // 验证是否升级为会员
        const userda = await customer_accountService.querySingle({ _id: sesiondata.userid }, { select: 'ismember' });
        if (userda && !userda.ismember) {
            const orderCode = await customer_orderService.buildorder(null, sesiondata.userid, money, 0, 0, [], 0, sesiondata.openid, 0, 0, shipname);
            return this.unifiedorder(sesiondata.openid, money, orderCode, "随餐饮艺升级会员", ctx);
        } else {
            throw Exceptions.known('您已是会员');
        }
    }
    // 解密数据
    async decryptData(sessionkey: any, encryptedData: any, iv: any) {
        let that = this;
        // base64 decode
        //encryptedData = encryptedData.replace(/ /g, '+')
        //iv = iv.replace(/ /g, '+');
        var sessionKey = new Buffer(sessionkey, 'base64');
        encryptedData = new Buffer(decodeURIComponent(encryptedData), 'base64');
        iv = new Buffer(decodeURIComponent(iv), 'base64');


        let decoded: any;
        try {
            // 解密
            var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv);
            // 设置自动 padding 为 true，删除填充补位
            decipher.setAutoPadding(true);
            decoded = decipher.update(encryptedData, 'binary', 'utf8');
            decoded += decipher.final('utf8');

            decoded = JSON.parse(decoded)

        } catch (err) {
            console.log('解密第一次');
            console.log(err);
            // try {
            //     // 解密
            //     var decipher = crypto.createDecipheriv('aes-128-cbc', sessionKey, iv);
            //     // 设置自动 padding 为 true，删除填充补位
            //     decipher.setAutoPadding(true);
            //     decoded = decipher.update(encryptedData, 'binary', 'utf8');
            //     decoded += decipher.final('utf8');

            //     decoded = JSON.parse(decoded)
            // } catch (error) {
            //     console.log('解密第二次');
            //     console.log(err);
            // }
            throw Exceptions.unknown(err);
        }

        if (decoded.watermark.appid !== that.appID) {
            console.log('appid不匹配');
            throw new Error('appid不匹配')
        }

        return decoded;
    }
    // 生成带参小程序二维码
    async buildqrcode(token: any) {
        let that = this;
        if (!token) {
            throw Exceptions.notoken();
        }
        const sesiondata = await usertokenService.getToken(token);
        const isexist = await qrcodeService.querySingle({ userid: sesiondata.userid });
        //if (!isexist) {
        console.log('第一次生成二维码');
        const user = await customer_accountService.querySingle({ _id: sesiondata.userid }, { select: 'code' });
        let url = that.qrcodeurl;
        if (user) {
            url += user.code;
        }
        try {
            let a = await QRCodetorul.toDataURL(url);
            let filepath = await fsfiles.baseimgwrite(a, token + '.png');
            qrcodeService.create({
                userid: sesiondata.userid,
                url: filepath,
                qrcodeurl: url
            });
            return filepath;
        } catch (error) {
            console.log(error)
            return null;
        }
        // } else {
        //     console.log('已生成过二维码，直接返回');
        //     return isexist.url;
        // }
    }
    async Qrcode(url: any, filename: any, pars: any) {
        try {
            let a = await QRCodetorul.toDataURL(url);
            let filepath = await fsfiles.baseimgwrite(a, filename + '.png');
            pars.url = filepath;
            pars.qrcodeurl = url;
            qrcodeService.create(pars);
            return filepath;
        } catch (error) {
            console.log(error)
            return null;
        }
    }
    // 根据位置获取经纬度
    async getlocation(address: any) {
        var that = this;
        var url = format(that.locationUrl, encodeURIComponent(address), encodeURIComponent(that.locationKey));
        console.log(url);
        let result: any = await new Promise((resolve, reject) => {
            //格式化请求地址

            //判断 本地存储的 access_token 是否有效
            return that.requestGet(url).then((data: any) => {
                console.log(data);
                var res = JSON.parse(data);
                resolve(res.result);
            });
        });

        return result;
    }
}

export const wechatService = new WechatService();